javascriptのテストのはなし:Sinon.JS(その1)
QUnitについてある程度分かった所で、今回はSinon.JSというライブラリを紹介します。
Sinon.JSをテストフレームワークと合わせて使う事で様々なテストができる様になります。特定のテストフレームワークには依存していないのでQUnit以外でも使えます。Sinon.JSのサイトに行けばいくつかのテストフレームワークのアダプタがあります。
Sinon.JSにはいくつかの機能があるので順に紹介します。今回はSpyとStubとMockの3つについてです。テストフレームワークにはQUnitを使っています。
準備
本題に入る前に、準備です。Sinon.JSのライブラリをhttp://sinonjs.org/からダウンロードして適当な位置に配置して、テストで使うhtmlにロードしておきます。
Spy
Spyは、メソッドが呼ばれた時の引数や戻り値、エラーなどを監視する事ができます。
これはSinon.JSのAPIドキュメントにあるそのままですが、jQuery.ajaxメソッドを監視してメソッドが呼ばれたかと、その時の引数についてテストしています。
sinon.spy()は下記例の様に、対象のオブジェクトとメソッド名を指定して作ればそのメソッドだけを監視します。他にもsinon.spy()、sinon.spy(myFunc)でspyオブジェクトを作成する事は出来ます。監視のスコープをどこまでにするかで使い分ければ良いと思います。
test("jQuery.getJSON", function(){ var spy = sinon.spy(jQuery, "ajax"); jQuery.getJSON("/some/resource"); ok(spy.calledOnce , "method is called once"); equal(spy.getCall(0).args[0].url, "/some/resource", "url parameter"); equal(spy.getCall(0).args[0].dataType, "json", "dataType parameter"); });
Spyには多くのメソッドがあるので全ては紹介しきれませんが、例えばwithArgsというメソッドを使うと特定の引数でメソッドが呼ばれたかをチェックできます。次の例だと引数"test"で呼ばれた回数をチェックしています。
test("spy.withArgs", function(){ var object = { method: function (str) {return str;} }; var spy = sinon.spy(object, "method"); object.method("test"); object.method("test"); object.method("sample"); object.method("test"); equal(spy.withArgs("test").callCount, 3, "method is called 3 times with argument \"test \""); });
Stub
特定のメソッドの振る舞いを変える事ができます。次の例では、stub.withArgs()とstub.returns()を合わせて使う事で、hogeのdoHogeメソッドで"hello"という引数がわたってきたらtrueを返し、goodbyeならfalseを返す様にしています。また、stub.throws()を使えば、doHogeメソッドが呼ばれたらエラーをスローする事が出来ます。
sinon.stub()もsinon.spy()の様に引数の数によってスコープが変わります。
test("stub.returns, stub.throws", function(){ var hoge = new Hoge(); var stub = sinon.stub(hoge, "doHoge"); stub.withArgs("hello").returns(true); stub.withArgs("goodbye").returns(false); equal(hoge.doHoge("hello"), true, "if arg is \"hello\", then method returns \"true\""); equal(hoge.doHoge("goodbye"), false, "if arg is \"goodbye\", then method returns \"false\""); equal(hoge.doHoge(""), undefined, "if arg is not \"hello\" or \"goodbye\", then method returns undefined"); stub.restore(); stub.throws(); raises(hoge.doHoge(""), Error, "method throws Error"); stub.restore(); });
hogeは次の様な簡単なクラスです。
var Hoge = function(){}; Hoge.prototype.doHoge = function(content){ return content; };
Mock
Mockは、対象とするメソッドが何回呼び出されるとか、どんな引数で呼び出されるかという期待値を設定してテストします。
sinon.mock()の場合は引数に対象のオブジェクトだけを渡して実行します。expect()を使って振る舞いに対する期待を記述し、verify()でチェックするという流れです。expect()やwithArgs()、twice()などのメソッドは全てexpectationを返すのでexpect().twice()の様にチェーンする事が出来ます。
次の例はdoHogeメソッドが引数に関係なく2回呼ばれるかをテストします。
test('mock.expects, mock.twice', function() { var hoge = new Hoge(); var mock = sinon.mock(hoge); mock.expects("doHoge").twice(); hoge.doHoge("test"); hoge.doHoge(""); ok(mock.verify(), "method \"doHoge\" is called twice"); });
また、次の例だと、doHogeが引数"test"を渡されて呼ばれるかのテストになります。この場合引数に空文字を渡してるのでテストは失敗します。
test('mock.expects, mock.withArgs ', function() { var hoge = new Hoge(); var mock = sinon.mock(hoge); mock.expects("doHoge").withArgs("test") hoge.doHoge(""); ok(mock.verify(), "method \"doHoge\" is called only 1 time with arg \"test\""); });
例示したテストの実行結果を一応貼っておきます。
今回はここまででです。雰囲気は伝わったでしょうか。次回はFake timerやFake serverについて書こうと思います。